﻿using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using Jet.Dal.Exceptions;
using Jet.Utilities;
using NLog;

namespace Jet.Dal.EF.Implementation
{
	/// <summary>
	/// Ef implementation of IUnitOfWork
	/// </summary>
	internal class EFUnitOfWork : DisposableResource, IEFUnitOfWork
	{
		private readonly DbContext _context;
		private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

		/// <summary>
		/// .ctor
		/// <exception cref="ArgumentNullException"> when context is null</exception>
		/// </summary>
		public EFUnitOfWork(DbContext context)
		{
			if (context == null)
			{
				throw new ArgumentNullException("context");
			}
			_context = context;
		}

		/// <summary>
		/// <see cref="DisposableResource"/>
		/// </summary>
		protected override void DisposeResources(bool disposeManagedResources)
		{
			_context.Dispose();
		}

		/// <summary>
		/// <see cref="IEFUnitOfWork"/>
		/// </summary>
		public IEFRepository<TEntity> GetRepository<TEntity>() where TEntity : class
		{
			var result = new EFRepository<TEntity>(_context);
			return result;
		}

		/// <summary>
		/// Save chanegs to database
		/// <exception cref="ValidationException">If context detecs any validation errors</exception>
		/// <exception cref="DbUpdateConcurrencyException">If data not updated as expected</exception>
		/// <exception cref="DbUpdateException">If update fails</exception>
		/// </summary>
		public void SaveChanges()
		{
			var errors = _context.GetValidationErrors().ToList();
			if (errors.Count > 0)
			{
				var info = (from error in errors
							from result in error.ValidationErrors
							select new Tuple<string, string>(result.PropertyName, result.ErrorMessage)).ToArray();

				throw new ValidationException(info);
			}
			try
			{
				_context.SaveChanges();
			}
			catch (DbUpdateConcurrencyException e)
			{				
				Logger.ErrorException("Concurrency error saving changes to db", e);
				throw new DataUpdateConcurrencyException("Error saving changes to db, see inner exception for details", e);
			}
			catch (DbUpdateException e)
			{
				Logger.ErrorException("Error saving changes to db", e);
				throw new DataUpdateException("Error saving changes to db, see inner exception for details", e);
			}
		}
	}
}
